package com.iwithlong.filter.handler;


import com.iwithlong.filter.exceptions.ForwardException;
import com.iwithlong.filter.http.HttpHandler;
import com.iwithlong.filter.http.HttpHandlerFactory;
import com.iwithlong.filter.http.RequestInfo;
import com.iwithlong.filter.http.ResponseInfo;
import com.iwithlong.filter.utils.PropertyUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

import static com.iwithlong.filter.utils.ForwardHttpUtils.*;


/**
 * write comments
 * User : weixi
 * Date : 2017/4/7 8:54
 * File : FineReportForwardHandler.java
 */
public class WeixinForwardHandler implements ForwardHandler {
	public static final String IS_ENCRYPT = "isEncrypt";
	public static final String PRO_KEY = "encrypt.key";
	public static final String DEFAULT_KEY = "2UzcNwi5PjE=";

	/**
	 * 帆软发请求时的sessionid key
	 */
	private static final String SESSION_ID = "sessionID";

	public WeixinForwardHandler(String forwardName){

	}
	@Override
	public boolean isValid ( String requestURI, HttpServletRequest request ) {
		return true;
	}

	@Override
	public RequestInfo buildForwardRequestInfo (HttpServletRequest request ) {
		RequestInfo requestInfo = new RequestInfo();
		String encrypt = request.getParameter(IS_ENCRYPT);
		boolean isEncrypt = StringUtils.isBlank(encrypt) ? false : Boolean.valueOf(encrypt);
		requestInfo.setEncrypt ( isEncrypt );
		requestInfo.setScheme ( request.getScheme() );
		requestInfo.setRequestURI ( request.getRequestURI());
		requestInfo.setRequestUrl (request.getRequestURL().toString()  );
		requestInfo.setServletPath (request.getServletPath()  );
		String pathInfo = request.getPathInfo();
		if (StringUtils.length(pathInfo) > 0) {
			requestInfo.setServletPath ( requestInfo.getServletPath ()+ pathInfo );
		}
		requestInfo.setQueryString ( request.getQueryString() );
		requestInfo.setEncodeQueryString ( getEncodeQueryString(request) );
		requestInfo.setRemoteIP ( getRemoteIP(request) );
		requestInfo.setMethod ( request.getMethod() );
		if(isEncrypt) {
			String encryptKey = PropertyUtils.getProperty("forward.properties", PRO_KEY, DEFAULT_KEY);
			requestInfo.setParams ( getParams(request, encryptKey) );
		}else{
			requestInfo.setParams ( getParams(request) );
		}
		requestInfo.setHeaders ( getHeaders(request) );
		requestInfo.setCookies ( getCookies(request) );


		return requestInfo;
	}

	@Override
	public ResponseInfo requestTargetServer (RequestInfo requestInfo, String forwardUrl, HttpServletRequest request ) {
		HttpHandler agentHandler = HttpHandlerFactory.getHttpHandler (HttpHandlerFactory.FINE_HANDLER);
		String targetServer = PropertyUtils.getProperty("agent", "fine.report.server");
		String reportServletPath = PropertyUtils.getProperty("agent", "report.servlet.path");
		//这里因为帆软报表系统原因，实际上包括ServletPath都是写死了的
		String targetURL = targetServer + reportServletPath;
		// add query string
		if (StringUtils.isNotBlank(requestInfo.getEncodeQueryString())) {
			targetURL = targetURL + "?" + requestInfo.getEncodeQueryString();
		}
		return this.getWeixinResponse(request, requestInfo, agentHandler, targetURL, targetServer, reportServletPath);
	}

	@Override
	public boolean writeResponse (ResponseInfo responseInfo, HttpServletRequest request, HttpServletResponse response ) {
		response.setContentType(responseInfo.getContentType());
		if (responseInfo.getBody() != null) {
			response.setHeader("Content-Encoding", "gzip");
			for( Header head:responseInfo.getHeaders ()){
				if(head.getName ().equals ( "Content-Disposition" )){
					response.setHeader ( "Content-Disposition" ,head.getValue ());
				}
			}
			try {
				response.getOutputStream().write(responseInfo.getBody());
			} catch ( IOException e ) {
				throw new ForwardException("", "写入返回数据失败:", e);
			}
		}
		return false;
	}

	@Override
	public boolean isFilter ( RequestInfo requestInfo, HttpServletRequest request ) {
		String targetPath = PropertyUtils.getProperty("agent.properties", requestInfo.getServletPath());
		String reportServletPath = PropertyUtils.getProperty("agent.properties", "report.servlet.path");
		if( StringUtils.isNotBlank(targetPath) || StringUtils.startsWith(requestInfo.getServletPath(), reportServletPath)){
			return true;
		}else{
			return false;
		}
	}


	private ResponseInfo getWeixinResponse(HttpServletRequest request, RequestInfo requestInfo, HttpHandler agentHandler, String targetURL, String targetServer, String reportServletPath) {
		ResponseInfo responseInfo = null;
		if ("post".equalsIgnoreCase(requestInfo.getMethod())) {

			// 创建一个通用的多部分解析器
			CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession()
					.getServletContext());

			// 判断 request 是否有文件上传,即多部分请求
			if (multipartResolver.isMultipart(request)) {
				MultipartHttpServletRequest multipartRequest = multipartResolver.resolveMultipart(request);
				Map<String, MultipartFile> filesMap = multipartRequest.getFileMap();
				Collection<MultipartFile> multipartFiles = filesMap.values();
				for (MultipartFile mf : multipartFiles) {
					//获取原始文件名
					String originalFilename = mf.getOriginalFilename();
					//获取文件流，可以进行处理
					InputStream in = null;
					try {
						in = mf.getInputStream();
					} catch ( IOException e ) {
						e.printStackTrace ();
					}
					responseInfo = agentHandler.postMultipart(targetURL, requestInfo, originalFilename, in);
				}
			} else {
				responseInfo = agentHandler.post(targetURL, requestInfo);
			}
		} else if ("get".equalsIgnoreCase(requestInfo.getMethod())) {
			responseInfo = agentHandler.get(targetURL, requestInfo);
		} else {
			throw new UnsupportedOperationException("不支持的代理方式:" + requestInfo.getMethod());
		}

		if (responseInfo.getBody() != null) {
			try {
				String text = new String ( responseInfo.getBody (), "gbk" );
				boolean isReplace = false;
				if ( StringUtils.indexOf ( text, targetServer ) != -1 ) {
					String localServer = request.getScheme () + "://" + request.getServerName () + ":" + request.getServerPort ();
					text = StringUtils.replace ( text, targetServer, localServer );
					isReplace = true;
				}
				if ( StringUtils.indexOf ( text, reportServletPath ) != -1 ) {
					String contextPath = request.getContextPath ();
					if ( StringUtils.isNotBlank ( contextPath ) ) {
						text = StringUtils.replace ( text, reportServletPath, contextPath + reportServletPath );
						isReplace = true;
					}
				}
				if ( isReplace ) {
					byte[] textBytes = text.getBytes ( "gbk" );
					responseInfo.setBody ( textBytes );
				}
			}catch ( IOException e ){
				e.printStackTrace ();
			}
				ByteArrayOutputStream baos = null;
				GZIPOutputStream gzipOs = null;
				try {
					baos = new ByteArrayOutputStream();
					gzipOs = new GZIPOutputStream(baos);
					gzipOs.write(responseInfo.getBody());
				} catch ( IOException e ) {
					e.printStackTrace ();
				} finally {
					IOUtils.closeQuietly(gzipOs);
					IOUtils.closeQuietly(baos);
				}

			responseInfo.setBody(baos.toByteArray());
			//            response.getOutputStream().write(baos.toByteArray());
		}
		return responseInfo;
	}
	/**
	 * 根据请求的url和参数获取唯一的缓存key
	 *
	 * @param requestInfo
	 * @param targetURL
	 * @return
	 */
	private String getCacheKey(RequestInfo requestInfo, String targetURL) {
		StringBuilder sb = new StringBuilder(targetURL);
		Map<String, String> params = requestInfo.getParams();
		if ( MapUtils.isNotEmpty(params)) {
			for (Map.Entry<String, String> entry : params.entrySet()) {
				if (StringUtils.equals(SESSION_ID, entry.getKey())) {
					continue;
				}
				sb.append(entry.getKey()).append(entry.getValue());
			}
		}
		return sb.toString();
		//        return UUID.fromString(sb.toString()).toString();
	}
}
